package com.inetools.gwt.gajaxs.client;

import java.util.Date;
import java.util.LinkedHashSet;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.i18n.client.impl.plurals.DefaultRule;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SimpleHtmlSanitizer;
import com.google.gwt.safehtml.shared.UriUtils;
import com.google.gwt.search.client.NewsResult;
import com.google.gwt.search.client.NewsResult.Image;
import com.google.gwt.search.client.NewsResult.RelatedStory;
import com.google.gwt.search.client.NewsSearch;
import com.google.gwt.search.client.Result;
import com.google.gwt.search.client.Search;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.CalendarUtil;
import com.inetools.gwetons.client.AttentionTracker;
import com.inetools.gwetons.client.AttentionTracker.AttentionTrackerListener;

public class NewsSearchResultsViewer implements SearchResultsControllerListener {

	// For attention handling ---------------------------------------------------------------------
	protected LinkedHashSet<SearchResultViewerStateListener> searchResultViewerStateListenerLinkedHashSet = 
		new LinkedHashSet<SearchResultViewerStateListener>();
	public boolean addSearchResultViewerStateListener( SearchResultViewerStateListener searchResultViewerStateListener) {
		if ( searchResultViewerStateListener == null) return false;
		return searchResultViewerStateListenerLinkedHashSet.add( searchResultViewerStateListener);
	}
	public boolean removeSearchResultViewerStateListener( SearchResultViewerStateListener searchResultViewerStateListener) {
		return searchResultViewerStateListenerLinkedHashSet.remove( searchResultViewerStateListener);
	}
	protected void notifySearchResultViewerReadyState() {
GWT.log( "Viewer is ready", null);
		for( SearchResultViewerStateListener searchResultViewerStateListener : searchResultViewerStateListenerLinkedHashSet) {
			searchResultViewerStateListener.viewerReady();
		} // for
	}
	protected void notifySearchResultViewerBusyState() {
GWT.log( "Viewer is busy", null);
		for( SearchResultViewerStateListener searchResultViewerStateListener : searchResultViewerStateListenerLinkedHashSet) {
			searchResultViewerStateListener.viewerBusy();
		} // for
	}

	protected AttentionTracker attentionTracker = new AttentionTracker();
	{
		attentionTracker.addAttentionTrackerListener( new AttentionTrackerListener() {
			@Override
			public void gotAttention() {
				notifySearchResultViewerBusyState();
			}
			@Override
			public void lostAttention() {
				notifySearchResultViewerReadyState();				
			}
		});
	}
	// --------------------------------------------------------------------------------------------
	
	protected VerticalPanel searchResultVerticalPanel;
//	protected SearchResultsControllerIntercepter searchResultsControllerIntercepter;
	
	
	
	public interface CssStyleRuleResourceBundle extends ClientBundle {
		public static final CssStyleRuleResourceBundle INSTANCE = 
			GWT.create( CssStyleRuleResourceBundle.class);
		@Source( "NewsSearchResultsViewer.css")
		public CssResource cssResource();
	}
	
	public NewsSearchResultsViewer( 
			VerticalPanel searchResultVerticalPanel 
//			SearchResultsControllerIntercepter searchResultsControllerIntercepter
			) 
	{
//		StyleInjector.injectStylesheet( CssStyleRuleResourceBundle.INSTANCE.cssResource().getText());
		if ( !CssStyleRuleResourceBundle.INSTANCE.cssResource().ensureInjected()) {
GWT.log( 
		"Failed to inject CSS style:\n\tname: " 
				+ CssStyleRuleResourceBundle.INSTANCE.cssResource().getName() 
				+ "\n\tstyle: " + CssStyleRuleResourceBundle.INSTANCE.cssResource().getText(),
		null);
		}
		this.searchResultVerticalPanel = searchResultVerticalPanel;
		searchResultVerticalPanel.addStyleName( "gajax-NewsSearchResult");
		
//		this.searchResultsControllerIntercepter = searchResultsControllerIntercepter;
		notifySearchResultViewerReadyState();
	}
	
	protected NewsSearchResultsViewerMessages newsSearchResultsViewerMessages = 
		GWT.create(NewsSearchResultsViewerMessages.class);

	protected com.google.gwt.user.client.ui.Image googleBrandingImage = 
		new com.google.gwt.user.client.ui.Image( "http://www.google.com/uds/css/small-logo.png");
	{
		Style googleBrandingImageStyle = googleBrandingImage.getElement().getStyle();
		googleBrandingImageStyle.setProperty( "display", "inline");
		googleBrandingImageStyle.setPropertyPx( "paddingTop", 1);
	};
	protected HTML googleBrandingTextHTML = new HTML( "powered by");
	{
		Style googleBrandingTextStyle= googleBrandingTextHTML.getElement().getStyle();
		googleBrandingTextStyle.setProperty( "display", "inline");
		googleBrandingTextStyle.setPropertyPx( "fontSize", 11);
		googleBrandingTextStyle.setProperty( "color", "#676767");
		googleBrandingTextStyle.setPropertyPx( "marginRight", 2);
		googleBrandingTextStyle.setPropertyPx( "paddingBottom", 2);
		googleBrandingTextStyle.setProperty( "textAlign", "right");
		googleBrandingTextStyle.setProperty( "verticalAlign", "top");
	};
	protected HTML googleBrandingHTML = new HTML();
	{
		Element googleBrandingHTMLElement = googleBrandingHTML.getElement();
		googleBrandingHTMLElement.appendChild( googleBrandingTextHTML.getElement());
		googleBrandingHTMLElement.appendChild( googleBrandingImage.getElement());
		googleBrandingHTMLElement.getStyle().setProperty( "float", "right");
	};

//TODO add my branding logo too addition to Google one
	
	/**
	 * Specify the number of news which appears as the opened tree item
	 */
	protected int openedTreeItemNum = 1;
	public int getOpenedTreeItemNum() {
		return openedTreeItemNum;
	}
	public void setOpenedTreeItemNum(int openedTreeItemNum) {
		this.openedTreeItemNum = openedTreeItemNum;
	}

	private final long ONE_DAY_LONG_MILLI_SEC = 24 * 60 * 60 * 1000;
	private final int ONE_HOUR_MILLI_SEC = 60 * 60 * 1000;

	protected class NewsTreeSelectionHandler implements
			SelectionHandler<TreeItem> {
		@Override
		public void onSelection(SelectionEvent<TreeItem> event) {
			// event.getSelectedItem().getElement().getStyle().setProperty(
			// "display", "block");

			TreeItem treeItemObj = event.getSelectedItem();
			Tree treeObj = event.getSelectedItem().getTree();
			boolean selectionIsItemFlag = false;
			for (int treeItemIndex = 0; treeItemIndex < treeObj.getItemCount(); treeItemIndex++) {
				if (treeItemObj == treeObj.getItem(treeItemIndex)) {
					selectionIsItemFlag = true;
					// treeItemObj.getElement().getStyle().setProperty(
					// "display", "block");
					NodeList<Element> divElementNodeList = treeItemObj
							.getElement().getElementsByTagName("div");
					if (divElementNodeList.getLength() < 1)
						return;
					divElementNodeList.getItem(0).getStyle().setProperty(
							"display", "block");
				}
			} // for
			if (!selectionIsItemFlag) { // Most likely it's item's item
				treeItemObj.getElement().getFirstChildElement().getStyle()
						.setProperty("display", "block");
			}
		}
	} // protected class NewsTreeSelectionHandler implements SelectionHandler<TreeItem>

	protected NewsTreeSelectionHandler newsTreeSelectionHandler = new NewsTreeSelectionHandler();

	protected TreeItem addNewsToTree(Tree newsTree, NewsResult newsResult,
			Date currentDate) {
		HorizontalPanel titleHorizontalPanel = new HorizontalPanel();

		SafeHtml titleSafeHtml = SimpleHtmlSanitizer.sanitizeHtml( newsResult.getTitle());
		String newsUrlStr = UriUtils.sanitizeUri( newsResult.getUrl());
		Anchor titleAnchor = new Anchor( titleSafeHtml, newsUrlStr, "_blank");
		// TODO In web mode, this didn't rel attribute. Instead, it set name
		// attribute to "nofollow".
		AnchorElement.as( titleAnchor.getElement()).setRel("nofollow");
		HTML titleHTML = new HTML();
		titleHTML.setWordWrap(true);
		titleHTML.getElement().appendChild( titleAnchor.getElement());
		// titleHorizontalPanel.add( titleAnchor);

		String publishedTimeInfo;
		Date publishedDate = newsResult.getPublishedDate();
		int elapsedDays = CalendarUtil.getDaysBetween(publishedDate, currentDate);
		if (elapsedDays > 0) {
			publishedTimeInfo = newsSearchResultsViewerMessages.daysPostNewsPublish(elapsedDays);
		} else {
			long elapsedMilliSec = (new Date()).getTime()
					- publishedDate.getTime();
			int elapsedTime = (new Long(elapsedMilliSec
					% ONE_DAY_LONG_MILLI_SEC)).intValue();
			int elapsedHours = elapsedTime / ONE_HOUR_MILLI_SEC;
			int elapsedMins = elapsedTime % ONE_HOUR_MILLI_SEC;
			elapsedMins = elapsedMins / (60 * 1000);
			publishedTimeInfo = 
				newsSearchResultsViewerMessages.timePostNewsPublish(elapsedHours, elapsedMins);
		}
		Label publishedTimeInfoLabel = new Label(publishedTimeInfo);
		publishedTimeInfoLabel.getElement().getStyle().setProperty( "marginLeft", "1em");
		publishedTimeInfoLabel
				.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
		publishedTimeInfoLabel.getElement().getStyle().setProperty("display", "inline");
		// titleHorizontalPanel.add( publishedTimeInfoLabel);
		titleHTML.getElement().appendChild(publishedTimeInfoLabel.getElement());
		titleHorizontalPanel.add(titleHTML);

		/*
		 * DisclosurePanel disclosurePanel = new DisclosurePanel(
		 * titleHorizontalPanel, false); disclosurePanel.setAnimationEnabled(
		 * true); disclosurePanel.addOpenHandler( new
		 * OpenHandler<DisclosurePanel>() {
		 * 
		 * @Override public void onOpen(OpenEvent<DisclosurePanel> event) {
		 * Object eventSource = event.getSource(); System.out.println(
		 * eventSource.getClass().getName()); if ( eventSource instanceof
		 * Anchor) { System.out.println( ((Anchor) eventSource).getHTML()); }
		 * else if ( eventSource instanceof com.google.gwt.user.client.ui.Image)
		 * { System.out.println( ((com.google.gwt.user.client.ui.Image)
		 * eventSource).getUrl()); }
		 * 
		 * DisclosurePanel targetDisclosurePanel = event.getTarget();
		 * targetDisclosurePanel.setOpen( true); } });
		 */
		VerticalPanel verticalPanel = new VerticalPanel();
		
		SafeHtml newsPublisherSafeHtml = SimpleHtmlSanitizer.sanitizeHtml( newsResult.getPublisher());
		HTML publisherHTML = new HTML( newsPublisherSafeHtml);
		verticalPanel.add( publisherHTML);
		
		SafeHtml newsContentSafeHtml = SimpleHtmlSanitizer.sanitizeHtml( newsResult.getContent());
		HTML contentHTML = new HTML( newsContentSafeHtml);
		verticalPanel.add( contentHTML);
		String relatedStoryClusterUrl = newsResult.getClusterUrl();
		if (relatedStoryClusterUrl != null) {
			JsArray<RelatedStory> relatedStoryJsArray = newsResult.getRelatedStories();
			if (relatedStoryJsArray.length() > 0) {
				Anchor relatedStoryClusterAnchor = 
					new Anchor("Related News", false, relatedStoryClusterUrl, "_blank");
				AnchorElement.as( relatedStoryClusterAnchor.getElement()).setRel("nofollow");

				VerticalPanel relatedStoryHolderVerticalPanel = new VerticalPanel();
				for (int relatedStoryIndex = 0; relatedStoryIndex < relatedStoryJsArray
						.length(); relatedStoryIndex++) {
					RelatedStory relatedStory = relatedStoryJsArray.get( relatedStoryIndex);
					VerticalPanel relatedStoryVerticalPanel = new VerticalPanel();

					SafeHtml relatedStoryTitleSafeHtml 
					= SimpleHtmlSanitizer.sanitizeHtml( relatedStory.getTitle());
					String relatedStoryUrlStr = UriUtils.sanitizeUri( relatedStory.getUrl());
					Anchor relatedStoryAnchor = 
						new Anchor( relatedStoryTitleSafeHtml, relatedStoryUrlStr,"_blank");
					AnchorElement.as( relatedStoryAnchor.getElement()).setRel( "nofollow");
					relatedStoryVerticalPanel.add( relatedStoryAnchor);
					SafeHtml relatedStoryPublisherSafeHtml 
					= SimpleHtmlSanitizer.sanitizeHtml( relatedStory.getPublisher());
					HTML relatedStoryPublisherHTML = new HTML( relatedStoryPublisherSafeHtml);
					relatedStoryVerticalPanel.add( relatedStoryPublisherHTML);

					relatedStoryHolderVerticalPanel.add( relatedStoryVerticalPanel);
				} // for

				Tree relatedStoryTree = new Tree();
				relatedStoryTree.addStyleDependentName("relatedNews");
				relatedStoryTree.addSelectionHandler( 
						new SelectionHandler<TreeItem>() {
							@Override
							public void onSelection( SelectionEvent<TreeItem> event) {
								// event.getSelectedItem().addStyleDependentName(
								// "relatedNews");
								event.getSelectedItem().getElement()
										.getFirstChildElement().getStyle()
										.setProperty("background", "none");
							}
						}
						);

				// relatedStoryTree.setAnimationEnabled( true);
				TreeItem relatedStoryTreeItem = relatedStoryTree.addItem( relatedStoryClusterAnchor);
				relatedStoryTreeItem.addItem( relatedStoryHolderVerticalPanel);
				verticalPanel.add( relatedStoryTree);
			}
		}

		HorizontalPanel horizontalPanel = new HorizontalPanel();
		horizontalPanel.add( verticalPanel);

		Image newsImage = newsResult.getImage();
		if (newsImage != null) {
			com.google.gwt.user.client.ui.Image image = 
				new com.google.gwt.user.client.ui.Image( newsImage.getTbUrl());
GWT.log("newsImage.getTitleNoFormatting() = " + newsImage.getTitleNoFormatting(), null);
			image.setTitle( newsImage.getTitleNoFormatting());
			image.getElement().getStyle().setPropertyPx("marginLeft", 7);

			Anchor imageAnchor = new Anchor();
			imageAnchor.setHref( newsImage.getUrl());
			imageAnchor.setTarget( "_blank");
			AnchorElement imageAnchorElement = AnchorElement.as( imageAnchor.getElement());
			imageAnchorElement.setRel( "nofollow");
			imageAnchorElement.setAttribute( "style", "float:right;");
			DOM.appendChild( imageAnchor.getElement(), image.getElement());

			horizontalPanel.add( imageAnchor);
		}

		TreeItem treeItem = newsTree.addItem( titleHorizontalPanel);
		treeItem.addItem( horizontalPanel);
		return treeItem;
	} // protected void addNewsToTree( Tree newsTree, NewsResult newsResult, Date currentDate)

	// Methods for SearchResultsController.SearchControllerListener -------------------------------
	@Override
	public void onSearchResultStackCleared() {
		// Do nothing
	}

	@Override
	public void onNewSearch() {
		// TODO Need to clear searchResultVerticalPanel
	}	
	
	@Override
	synchronized public void onSearchResultStackFilled( 
			Search search, 
			LinkedHashSet<Result> searchResultStackLinkedHashSet, 
			LinkedHashSet<Widget> attributionWidgetLinkedHashSet
			) 
	{
		if ((searchResultVerticalPanel == null) || !(search instanceof NewsSearch) ){
// TODO add error message handling (interface) feature
			return;
		}
		
		notifySearchResultViewerBusyState();
		
		Tree newsTree = new Tree();
		newsTree.addSelectionHandler( newsTreeSelectionHandler);
		
		Date currentDate = new Date();
		int resultIndex = 0;
		for (Result result : searchResultStackLinkedHashSet) {
			TreeItem treeItem = 
				addNewsToTree(newsTree, (NewsResult) result, currentDate);
			if ( resultIndex < 1) {
				newsTree.setSelectedItem( treeItem, true);
			}
			if ( resultIndex < openedTreeItemNum) {
				treeItem.setState( true);
			}
			resultIndex++;
		} // for
		
		// Add attentionTracker after finishing to add tree items since firing selection event during adding
		newsTree.addMouseOverHandler( attentionTracker);
		newsTree.addMouseOutHandler( attentionTracker);
		newsTree.addBlurHandler( attentionTracker);
		newsTree.addFocusHandler( attentionTracker);

		searchResultVerticalPanel.clear();
		searchResultVerticalPanel.add(newsTree);
		
		FlowPanel attributionWidgetFlowPanel = new FlowPanel();
		attributionWidgetFlowPanel.getElement().getStyle().setProperty( "textAlign", "right");
		if ( attributionWidgetLinkedHashSet.size() > 0) {
			for( Widget attributionWidget : attributionWidgetLinkedHashSet) {
				attributionWidget.getElement().getStyle().setProperty( "float", "right");
				attributionWidgetFlowPanel.add( attributionWidget);
			} // for
		}
		attributionWidgetFlowPanel.add( googleBrandingHTML);
		searchResultVerticalPanel.add( attributionWidgetFlowPanel);
		
		notifySearchResultViewerReadyState();
	}
	// --------------------------------------------------------------------------------------------
	
}
